<!doctype html>
<html>
<head>
<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
<title>NodeJs.md</title><link href='https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700,700italic&subset=latin,cyrillic-ext,cyrillic,latin-ext' rel='stylesheet' type='text/css'><style type='text/css'>html, body {overflow-x: initial !important;}.CodeMirror { height: auto; }
.CodeMirror-scroll { overflow-y: hidden; overflow-x: auto; }
.CodeMirror-lines { padding: 4px 0px; }
.CodeMirror pre { }
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; }
.CodeMirror-gutters { border-right: 1px solid rgb(221, 221, 221); background-color: rgb(247, 247, 247); white-space: nowrap; }
.CodeMirror-linenumbers { }
.CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153); }
.CodeMirror div.CodeMirror-cursor { border-left: 1px solid black; z-index: 3; }
.CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; }
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { width: auto; border: 0px; background: rgb(119, 238, 119); z-index: 1; }
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite { }
.cm-tab { display: inline-block; }
.cm-s-typora-default .cm-header, .cm-s-typora-default .cm-property { color: rgb(217, 79, 138); }
.cm-s-typora-default pre.cm-header1:not(.cm-atom) :not(.cm-overlay) { font-size: 2rem; line-height: 2rem; }
.cm-s-typora-default pre.cm-header2:not(.cm-atom) :not(.cm-overlay) { font-size: 1.4rem; line-height: 1.4rem; }
.cm-s-typora-default .cm-atom, .cm-s-typora-default .cm-number { color: rgb(149, 132, 134); }
.cm-s-typora-default .cm-table-row, .cm-s-typora-default .cm-block-start { font-family: monospace; }
.cm-s-typora-default .cm-comment, .cm-s-typora-default .cm-code { color: rgb(74, 90, 159); font-family: monospace; }
.cm-s-typora-default .cm-tag { color: rgb(169, 68, 66); }
.cm-s-typora-default .cm-string { color: rgb(126, 134, 169); }
.cm-s-typora-default .cm-link { color: rgb(196, 122, 15); text-decoration: underline; }
.cm-s-typora-default .cm-variable-2, .cm-s-typora-default .cm-variable-1 { color: inherit; }
.cm-s-typora-default .cm-overlay { font-size: 1rem; font-family: monospace; }
.CodeMirror.cm-s-typora-default div.CodeMirror-cursor { border-left: 3px solid rgb(228, 98, 154); }
.cm-s-typora-default .CodeMirror-activeline-background { left: -60px; right: -30px; background: rgba(204, 204, 204, 0.2); }
.cm-s-typora-default .CodeMirror-gutters { border-right: none; background-color: inherit; }
.cm-s-typora-default .cm-trailing-space-new-line::after, .cm-startspace::after, .cm-starttab .cm-tab::after { content: "•"; position: absolute; left: 0px; opacity: 0; font-family: LetterGothicStd, monospace; }
.os-windows .cm-startspace::after, .os-windows .cm-starttab .cm-tab::after { left: -0.1em; }
.cm-starttab .cm-tab::after { content: " "; }
.cm-startspace, .cm-tab, .cm-starttab, .cm-trailing-space-a, .cm-trailing-space-b, .cm-trailing-space-new-line { font-family: monospace; position: relative; }
.cm-s-typora-default .cm-trailing-space-new-line::after { content: "↓"; opacity: 0.3; }
.cm-s-inner .cm-keyword { color: rgb(119, 0, 136); }
.cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgb(34, 17, 153); }
.cm-s-inner .cm-number { color: rgb(17, 102, 68); }
.cm-s-inner .cm-def { color: rgb(0, 0, 255); }
.cm-s-inner .cm-variable { color: black; }
.cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170); }
.cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85); }
.cm-s-inner .cm-property { color: black; }
.cm-s-inner .cm-operator { color: rgb(152, 26, 26); }
.cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgb(170, 85, 0); }
.cm-s-inner .cm-string { color: rgb(170, 17, 17); }
.cm-s-inner .cm-string-2 { color: rgb(255, 85, 0); }
.cm-s-inner .cm-meta { color: rgb(85, 85, 85); }
.cm-s-inner .cm-qualifier { color: rgb(85, 85, 85); }
.cm-s-inner .cm-builtin { color: rgb(51, 0, 170); }
.cm-s-inner .cm-bracket { color: rgb(153, 153, 119); }
.cm-s-inner .cm-tag { color: rgb(17, 119, 0); }
.cm-s-inner .cm-attribute { color: rgb(0, 0, 204); }
.cm-s-inner .cm-header, .cm-s-inner.cm-header { color: blue; }
.cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgb(0, 153, 0); }
.cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgb(153, 153, 153); }
.cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgb(0, 0, 204); }
.cm-negative { color: rgb(221, 68, 68); }
.cm-positive { color: rgb(34, 153, 34); }
.cm-header, .cm-strong { font-weight: bold; }
.cm-del { text-decoration: line-through; }
.cm-em { font-style: italic; }
.cm-link { text-decoration: underline; }
.cm-error { color: rgb(255, 0, 0); }
.cm-invalidchar { color: rgb(255, 0, 0); }
.cm-constant { color: rgb(38, 139, 210); }
.cm-defined { color: rgb(181, 137, 0); }
div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0); }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34); }
.cm-s-inner .CodeMirror-activeline-background { background: inherit; }
.CodeMirror { position: relative; overflow: hidden; }
.CodeMirror-scroll { margin-bottom: -30px; margin-right: -30px; padding-bottom: 30px; padding-right: 30px; height: 100%; outline: none; position: relative; box-sizing: content-box; }
.CodeMirror-sizer { position: relative; }
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; }
.CodeMirror-vscrollbar { right: 0px; top: 0px; overflow-x: hidden; overflow-y: scroll; }
.CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow-y: hidden; overflow-x: scroll; }
.CodeMirror-scrollbar-filler { right: 0px; bottom: 0px; }
.CodeMirror-gutter-filler { left: 0px; bottom: 0px; }
.CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 30px; z-index: 3; }
.CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; }
.CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; }
.CodeMirror-lines { cursor: text; }
.CodeMirror pre { border-radius: 0px; border-width: 0px; background: transparent; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; word-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible; }
.CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; }
.CodeMirror-code pre { border-right: 30px solid transparent; width: fit-content; }
.CodeMirror-wrap .CodeMirror-code pre { border-right: none; width: auto; }
.CodeMirror-linebackground { position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 0; }
.CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; }
.CodeMirror-widget { }
.CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; }
.CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden; }
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right: none; width: 0px; }
.CodeMirror div.CodeMirror-cursor { visibility: hidden; }
.CodeMirror-focused div.CodeMirror-cursor { visibility: inherit; }
.CodeMirror-selected { background: rgb(217, 217, 217); }
.CodeMirror-focused .CodeMirror-selected { background: rgb(215, 212, 240); }
.cm-searching { background: rgba(255, 255, 0, 0.4); }
.CodeMirror span { }
@media print { 
  .CodeMirror div.CodeMirror-cursor { visibility: hidden; }
}
.CodeMirror-lint-markers { width: 16px; }
.CodeMirror-lint-tooltip { background-color: infobackground; border: 1px solid black; border-radius: 4px; color: infotext; font-family: monospace; overflow: hidden; padding: 2px 5px; position: fixed; white-space: pre-wrap; z-index: 10000; max-width: 600px; opacity: 0; transition: opacity 0.4s; font-size: 0.8em; }
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { background-position: left bottom; background-repeat: repeat-x; }
.CodeMirror-lint-mark-error { background-image: url(""); }
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { background-position: center center; background-repeat: no-repeat; cursor: pointer; display: inline-block; height: 16px; width: 16px; vertical-align: middle; position: relative; }
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { padding-left: 18px; background-position: left top; background-repeat: no-repeat; }
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { background-image: url(""); }
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { background-image: url(""); }
.CodeMirror-lint-marker-multiple { background-image: url(""); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; }


html { font-size: 14px; background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); }
body { margin: 0px; padding: 0px; height: auto; bottom: 0px; top: 0px; left: 0px; right: 0px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1rem; line-height: 1.42857; overflow-x: hidden; background: inherit; }
a:active, a:hover { outline: 0px; }
.in-text-selection, ::selection { background: rgb(181, 214, 252); text-shadow: none; }
#write { margin: 0px auto; height: auto; width: inherit; word-break: normal; word-wrap: break-word; position: relative; padding-bottom: 70px; white-space: pre-wrap; overflow-x: auto; }
.for-image #write { padding-left: 8px; padding-right: 8px; }
body.typora-export { padding-left: 30px; padding-right: 30px; }
@media screen and (max-width: 500px) { 
  body.typora-export { padding-left: 0px; padding-right: 0px; }
  .CodeMirror-sizer { margin-left: 0px !important; }
  .CodeMirror-gutters { display: none !important; }
}
.typora-export #write { margin: 0px auto; }
#write > p:first-child, #write > ul:first-child, #write > ol:first-child, #write > pre:first-child, #write > blockquote:first-child, #write > div:first-child, #write > table:first-child { margin-top: 30px; }
#write li > table:first-child { margin-top: -20px; }
img { max-width: 100%; vertical-align: middle; }
input, button, select, textarea { color: inherit; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: inherit; line-height: inherit; font-family: inherit; }
input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; }
::before, ::after, * { box-sizing: border-box; }
#write p, #write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write div, #write pre { width: inherit; }
#write p, #write h1, #write h2, #write h3, #write h4, #write h5, #write h6 { position: relative; }
h1 { font-size: 2rem; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.6rem; }
h4 { font-size: 1.4rem; }
h5 { font-size: 1.2rem; }
h6 { font-size: 1rem; }
p { -webkit-margin-before: 1rem; -webkit-margin-after: 1rem; -webkit-margin-start: 0px; -webkit-margin-end: 0px; }
.mathjax-block { margin-top: 0px; margin-bottom: 0px; -webkit-margin-before: 0rem; -webkit-margin-after: 0rem; }
.hidden { display: none; }
.md-blockmeta { color: rgb(204, 204, 204); font-weight: bold; font-style: italic; }
a { cursor: pointer; }
#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; margin: 4px 0px 0px; }
tr { break-inside: avoid; break-after: auto; }
thead { display: table-header-group; }
table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; }
table.md-table td { min-width: 80px; }
.CodeMirror-gutters { border-right: 0px; background-color: inherit; }
.CodeMirror { text-align: left; }
.CodeMirror-placeholder { opacity: 0.3; }
.CodeMirror pre { padding: 0px 4px; }
.CodeMirror-lines { padding: 0px; }
div.hr:focus { cursor: none; }
pre { white-space: pre-wrap; }
.CodeMirror-gutters { margin-right: 4px; }
.md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background: inherit; position: relative !important; }
.md-diagram-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; }
.md-fences .CodeMirror.CodeMirror-wrap { top: -1.6em; margin-bottom: -1.6em; }
.md-fences.mock-cm { white-space: pre-wrap; }
.show-fences-line-number .md-fences { padding-left: 0px; }
.show-fences-line-number .md-fences.mock-cm { padding-left: 40px; }
.footnotes { opacity: 0.8; font-size: 0.9rem; padding-top: 1em; padding-bottom: 1em; }
.footnotes + .footnotes { margin-top: -1em; }
.md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; background: transparent; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; -webkit-tap-highlight-color: transparent; line-height: normal; font-weight: normal; text-align: left; box-sizing: content-box; direction: ltr; }
li div { padding-top: 0px; }
blockquote { margin: 1rem 0px; }
li p, li .mathjax-block { margin: 0.5rem 0px; }
li { margin: 0px; position: relative; }
blockquote > :last-child { margin-bottom: 0px; }
blockquote > :first-child { margin-top: 0px; }
.footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; }
@media print { 
  html, body { height: 100%; }
  .typora-export * { -webkit-print-color-adjust: exact; }
  h1, h2, h3, h4, h5, h6 { break-after: avoid-page; orphans: 2; }
  p { orphans: 4; }
  html.blink-to-pdf { font-size: 13px; }
  .typora-export #write { padding-left: 1cm; padding-right: 1cm; }
  .typora-export #write::after { height: 0px; }
  @page { margin: 20mm 0mm; }
}
.footnote-line { margin-top: 0.714em; font-size: 0.7em; }
a img, img a { cursor: pointer; }
pre.md-meta-block { font-size: 0.8rem; min-height: 2.86rem; white-space: pre-wrap; background: rgb(204, 204, 204); display: block; overflow-x: hidden; }
p .md-image:only-child { display: inline-block; width: 100%; text-align: center; }
#write .MathJax_Display { margin: 0.8em 0px 0px; }
.mathjax-block { white-space: pre; overflow: hidden; width: 100%; }
p + .mathjax-block { margin-top: -1.143rem; }
.mathjax-block:not(:empty)::after { display: none; }
[contenteditable="true"]:active, [contenteditable="true"]:focus { outline: none; box-shadow: none; }
.task-list { list-style-type: none; }
.task-list-item { position: relative; padding-left: 1em; }
.task-list-item input { position: absolute; top: 0px; left: 0px; }
.math { font-size: 1rem; }
.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-radius: 10px; }
.md-toc-content { position: relative; margin-left: 0px; }
.md-toc::after, .md-toc-content::after { display: none; }
.md-toc-item { display: block; color: rgb(65, 131, 196); text-decoration: none; }
.md-toc-inner:hover { }
.md-toc-inner { display: inline-block; cursor: pointer; }
.md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: bold; }
.md-toc-h2 .md-toc-inner { margin-left: 2em; }
.md-toc-h3 .md-toc-inner { margin-left: 4em; }
.md-toc-h4 .md-toc-inner { margin-left: 6em; }
.md-toc-h5 .md-toc-inner { margin-left: 8em; }
.md-toc-h6 .md-toc-inner { margin-left: 10em; }
@media screen and (max-width: 48em) { 
  .md-toc-h3 .md-toc-inner { margin-left: 3.5em; }
  .md-toc-h4 .md-toc-inner { margin-left: 5em; }
  .md-toc-h5 .md-toc-inner { margin-left: 6.5em; }
  .md-toc-h6 .md-toc-inner { margin-left: 8em; }
}
a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; }
.footnote-line a:not(.reversefootnote) { color: inherit; }
.md-attr { display: none; }
.md-fn-count::after { content: "."; }
.md-tag { opacity: 0.5; }
.md-comment { color: rgb(162, 127, 3); opacity: 0.8; font-family: monospace; }
code { text-align: left; }
h1 .md-tag, h2 .md-tag, h3 .md-tag, h4 .md-tag, h5 .md-tag, h6 .md-tag { font-weight: initial; opacity: 0.35; }
a.md-print-anchor { border-width: initial !important; border-style: none !important; border-color: initial !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: none !important; background: transparent !important; text-decoration: initial !important; text-shadow: initial !important; }
.md-inline-math .MathJax_SVG .noError { display: none !important; }
.mathjax-block .MathJax_SVG_Display { text-align: center; margin: 1em 0em; position: relative; text-indent: 0px; max-width: none; max-height: none; min-height: 0px; min-width: 100%; width: auto; display: block !important; }
.MathJax_SVG_Display, .md-inline-math .MathJax_SVG_Display { width: auto; margin: inherit; display: inline-block !important; }
.MathJax_SVG .MJX-monospace { font-family: monospace; }
.MathJax_SVG .MJX-sans-serif { font-family: sans-serif; }
.MathJax_SVG { display: inline; font-style: normal; font-weight: normal; line-height: normal; zoom: 90%; text-indent: 0px; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; padding: 0px; margin: 0px; }
.MathJax_SVG * { transition: none; }


@font-face { font-family: "PT Serif"; font-style: normal; font-weight: normal; src: local("PT Serif"), local("PTSerif-Regular"), url("./newsprint/PT_Serif-Web-Regular.ttf"); }
@font-face { font-family: "PT Serif"; font-style: italic; font-weight: normal; src: local("PT Serif"), local("PTSerif-Italic"), url("./newsprint/PT_Serif-Web-Italic.ttf"); }
@font-face { font-family: "PT Serif"; font-style: normal; font-weight: bold; src: local("PT Serif"), local("PTSerif-Bold"), url("./newsprint/PT_Serif-Web-Bold.ttf"); }
@font-face { font-family: "PT Serif"; font-style: italic; font-weight: bold; src: local("PT Serif"), local("PTSerif-BoldItalic"), url("./newsprint/PT_Serif-Web-BoldItalic.ttf"); }
html { font-size: 16px; }
html, body { background-color: rgb(243, 242, 238); font-family: "PT Serif", "Times New Roman", Times; color: rgb(31, 9, 9); line-height: 1.5em; }
#write { max-width: 40em; }
ol, ul { list-style: none; }
blockquote, q { quotes: none; }
blockquote::before, blockquote::after, q::before, q::after { content: none; }
table { border-collapse: collapse; border-spacing: 0px; }
h1, h2, h3, h4, h5, h6 { font-weight: bold; }
h1 { font-size: 1.875em; line-height: 1.6em; margin-top: 2em; }
h2, h3 { font-size: 1.3125em; line-height: 1.15; margin-top: 2.28571em; margin-bottom: 1.15em; }
h3 { font-weight: normal; }
h4 { font-size: 1.125em; margin-top: 2.67em; }
h5, h6 { font-size: 1em; }
h1 { border-bottom: 1px solid; margin-bottom: 1.875em; padding-bottom: 0.8125em; }
a { text-decoration: none; color: rgb(6, 85, 136); }
a:hover, a:active { text-decoration: underline; }
p, blockquote, .md-fences { margin-bottom: 1.5em; }
h1, h2, h3, h4, h5, h6 { margin-bottom: 1.5em; }
blockquote { font-style: italic; border-left: 5px solid; margin-left: 2em; padding-left: 1em; }
ul, ol { margin: 0px 0px 1.5em 1.5em; }
ol li { list-style-type: decimal; list-style-position: outside; }
ul li { list-style-type: disc; list-style-position: outside; }
.md-meta, .md-before, .md-after { color: rgb(153, 153, 153); }
table { margin-bottom: 1.5em; font-size: 1em; }
thead th, tfoot th { padding: 0.25em 0.25em 0.25em 0.4em; text-transform: uppercase; }
th { text-align: left; }
td { vertical-align: top; padding: 0.25em 0.25em 0.25em 0.4em; }
code, .md-fences { background-color: rgb(218, 218, 218); padding-left: 1ch; padding-right: 1ch; }
.md-fences { margin-left: 2em; margin-bottom: 3em; }
.md-fences .CodeMirror.CodeMirror-wrap { top: -0.8em; }
pre, code, tt { font-size: 0.875em; line-height: 1.71428em; }
h1 { line-height: 1.3em; font-weight: normal; margin-bottom: 0.5em; }
p + ul, p + ol { margin-top: -1em; }
h3 + ul, h4 + ul, h5 + ul, h6 + ul, h3 + ol, h4 + ol, h5 + ol, h6 + ol { margin-top: 0.5em; }
li > ul, li > ol { margin-top: inherit; }
h2, h3 { margin-bottom: 0.75em; }
hr { border-top: none; border-right: none; border-bottom: 1px solid; border-left: none; }
h1 { border-color: rgb(197, 197, 197); }
blockquote { border-color: rgb(186, 186, 186); color: rgb(101, 101, 101); }
thead.md-table-edit { background-color: transparent; }
thead { background-color: rgb(218, 218, 218); }
tr:nth-child(2n) { background: rgb(232, 231, 231); }
hr { border-color: rgb(197, 197, 197); }
.task-list { padding-left: 1rem; }
.task-list-item { padding-left: 1.5rem; list-style-type: none; }
.task-list-item input::before { content: "√"; display: inline-block; width: 1.25rem; height: 1.6rem; vertical-align: middle; text-align: center; color: rgb(221, 221, 221); background-color: rgb(243, 242, 238); }
.task-list-item input:checked::before, .task-list-item input[checked]::before { color: inherit; }
#write pre.md-meta-block { min-height: 1.875rem; color: rgb(85, 85, 85); border: 0px; background: transparent; margin-left: 1em; margin-top: 1em; }
.md-image > .md-meta { color: rgb(155, 81, 70); }
.md-expand.md-image > .md-meta { background-color: rgba(255, 255, 255, 0.65098); }
.md-image > .md-meta { font-family: Menlo, "Ubuntu Mono", Consolas, "Courier New", "Microsoft Yahei", "Hiragino Sans GB", "WenQuanYi Micro Hei", sans-serif; }
#write > h3.md-focus::before { left: -1.5rem; color: rgb(153, 153, 153); border-color: rgb(153, 153, 153); }
#write > h4.md-focus::before { left: -1.5rem; top: 0.25rem; color: rgb(153, 153, 153); border-color: rgb(153, 153, 153); }
#write > h5.md-focus::before { left: -1.5rem; color: rgb(153, 153, 153); border-color: rgb(153, 153, 153); }
#write > h6.md-focus::before { left: -1.5rem; top: 0.3125rem; color: rgb(153, 153, 153); border-color: rgb(153, 153, 153); }
.md-toc:focus .md-toc-content { margin-top: 19px; }
.md-toc-content:empty::before { color: rgb(6, 85, 136); }
.md-toc-item { color: rgb(6, 85, 136); }
#write div.md-toc-tooltip { background-color: rgb(243, 242, 238); }
#outline-dropmenu { background-color: rgb(243, 242, 238); box-shadow: rgba(0, 0, 0, 0.372549) 0px 6px 12px; }
.pin-outline #outline-dropmenu { background: inherit; box-shadow: none; border-right: 1px dashed; }
.pin-outline #outline-dropmenu:hover .outline-title-wrapper { border-left: 1px dashed; }
.outline-item:hover { background-color: rgb(218, 218, 218); border-left: 28px solid rgb(218, 218, 218); border-right: 18px solid rgb(218, 218, 218); }
.typora-node .outline-item:hover { border-right: 28px solid rgb(218, 218, 218); }
.outline-expander::before { content: ""; font-family: FontAwesome; font-size: 14px; top: 1px; }
.outline-expander:hover::before, .outline-item-open > .outline-item > .outline-expander::before { content: ""; }
.modal-content { background-color: rgb(243, 242, 238); }
.auto-suggest-container ul li { list-style-type: none; }
.megamenu-menu, #top-titlebar, #top-titlebar *, .megamenu-content { background: rgb(243, 242, 238); color: rgb(31, 9, 9); }
.megamenu-menu-header { border-bottom: 1px dashed rgb(32, 43, 51); }
.megamenu-menu { box-shadow: none; border-right: 1px dashed; }
header, .context-menu, .megamenu-content, footer { font-family: "PT Serif", "Times New Roman", Times; color: rgb(31, 9, 9); }
#megamenu-back-btn { color: rgb(31, 9, 9); border-color: rgb(31, 9, 9); }
.megamenu-menu-header #megamenu-menu-header-title::before { color: rgb(31, 9, 9); }
.megamenu-menu-list li a:hover, .megamenu-menu-list li a.active { color: inherit; background-color: rgb(232, 231, 223); }
.long-btn:hover { background-color: rgb(232, 231, 223); }
#recent-file-panel tbody tr:nth-child(2n-1) { background-color: transparent !important; }
.megamenu-menu-panel tbody tr:hover td:nth-child(2) { color: inherit; }
.megamenu-menu-panel .btn { background-color: rgb(210, 209, 209); }
.btn-default { background-color: transparent; }
.typora-sourceview-on #toggle-sourceview-btn, .show-word-count #footer-word-count { background: rgb(199, 197, 197); }
#typora-quick-open { background-color: inherit; }
.md-diagram-panel { margin-left: -1ch; margin-top: 24px; }






</style>
</head>
<body class='typora-export' >
<div  id='write'  class = 'is-node'><p></p><h1><a name='header-c7' class='md-header-anchor '></a>基本概念</h1><h2><a name='header-c8' class='md-header-anchor '></a>前端的主要工作是什么？</h2><ol start='' ><li>绘制网页的页面（HTML）</li><li>写CSS样式美化页面、写JS做网页交互（更多的是网页的特效）</li><li>借助于 XHR（$.ajax   $.get   $.post）请求后端的接口；实现前后端分离开发</li><li>使用前端的（框架）去完成界面的开发</li><li>总结：前端的主要工作：用户能看到的东西，基本上都是前端做出来；</li></ol><h2><a name='header-c25' class='md-header-anchor '></a>后端的主要工作是什么？</h2><ol start='' ><li>后端为前端程序员暴露API接口；</li><li>后端也要操作数据库；</li><li>优化后端业务的性能；</li></ol><h2><a name='header-c36' class='md-header-anchor '></a>NodeJs主要做什么？</h2><ul><li>目前这个阶段主要用来写后端的接口</li><li>浏览器的JS在V8引擎中运行，node就是把V8引擎搬到了后台,即让JS能够在后台运行。</li></ul><h2><a name='header-c44' class='md-header-anchor '></a>NodeJs安装版本:LTS 和 Current 区别</h2><ol start='' ><li>LTS 是长期稳定版的意思（这个安装包用起来比较稳定）【推荐在企业中使用】</li><li>Current 是最新特征版，这个安装包中有最新的Node特性，但是，可能有一些潜藏的Bug未解决；【推荐学习或尝鲜去使用】</li></ol><h2><a name='header-c52' class='md-header-anchor '></a>node的组成部分</h2><h3><a name='header-c53' class='md-header-anchor '></a>浏览器中的JS组成</h3><ul><li>ECMAScript核心</li><li>DOM</li><li>BOM</li></ul><p>node中的JS组成部分</p><ul><li><p>ECMAScript（没有DOM，BOM）</p><p>node中没有浏览器，所以不需要DOM和BOM</p></li><li><p>全局成员</p><p>console.log，setTimeout，setInterval</p></li><li><p>模块系统</p><p>node中的一些核心模块</p></li></ul><h1><a name='header-c82' class='md-header-anchor '></a>ECMAScript6</h1><p>var定义变量的缺点：</p><ul><li>1.变量提升的问题</li><li>2.没有块级作用域（作用域不受花括号限制）</li></ul><h2><a name='header-c92' class='md-header-anchor '></a>let与const</h2><ul><li><p>let 没有变量的提升用来代替var定义变量</p><p>有{} 作用域</p></li><li><p>const 没有变量的提升用来定义常量</p><p>有{} 作用域 初始化的时候一定要赋值 , 赋值之后不能重新赋值</p><p>如：const a = 1; a = 2; 发生错误</p></li></ul><h2><a name='header-c106' class='md-header-anchor '></a>解构赋值</h2><p>所谓的解构赋值，就是把 某个对象中的属性，当作变量，给解放出来，这样，今后就能够当作变量直接使用了</p><ul><li>可以使用 <code>:</code>为解构出来的变量重命名</li></ul><pre class='md-fences mock-cm' style='display:block;position:relative'>  // 对象的解构赋值,按照属性名解构
  const { name : name123, age, gender } = person
  console.log(name123)
  //数组的解构复制,按照顺序解构
  let arr: number[] = [2,3]
  let [num1,num2,num3] = arr
  console.log([num1,num2,num3])//num3=undfine</pre><h2><a name='header-c114' class='md-header-anchor '></a>字符串扩展</h2><ol start='' ><li><p>模板字符串</p></li><li><p>startsWith() 和 endsWith()</p><p>​	startsWith() 用来判断字符串，是否以指定的字符开头，如果是，返回值是 true，否则返回 false</p><p>​	endsWith() 用来判断字符串，是否以指定的字符结尾；如果是，返回值是 true，否则返回 false</p></li><li><p>padStart() 和 padEnd()</p><p>​	在字符串前面或者后面追加字符串;</p><p>​	参数一：表示填充完后长度为几位; 参数2：表示用什么填充</p></li></ol><h2><a name='header-c133' class='md-header-anchor '></a>rest参数 和 字符串的扩展</h2><ul><li>rest参数： 在形参调用的位置 使用 ...args args是一个数组</li><li>字符串的扩展： 在实参的位置 使用 ...arr</li></ul><pre class='md-fences mock-cm' style='display:block;position:relative'>  function show(...args){  // rest参数

  }

  var arr = [1,2,3,4]
  show(...arr);  //字符串的扩展</pre><h2><a name='header-c142' class='md-header-anchor '></a>箭头函数</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>&lt;!-- 标准定义   --&gt;
var add = () =&gt; {}
&lt;!-- 只有一个参数   --&gt;
var add = (x) =&gt; {console.log(x)}
var add = x =&gt; console.log(x)
&lt;!-- 只有一个参数,且只有一个返回值   --&gt;
var add = x =&gt; {return x}
var add = x =&gt; x
&lt;!-- 两个参数    --&gt;
var add = (x,y) =&gt; {return x+y}
var add = (x,y) =&gt; x + y</pre><ol start='' ><li>箭头函数的特性： 箭头函数内部的 this, 永远和 箭头函数外部的 this 保持一致；</li><li>箭头函数，本质上就是一个匿名函数</li><li>注意： 如果我们省略了 右侧的 { }， 那么，默认就会把 右侧函数体中的代码执行结果，返回出去     (x, y) =&gt; { return  x + y }   可以简写成    (x, y) =&gt; x + y</li></ol><h2><a name='header-c154' class='md-header-anchor '></a>文件操作</h2><h3><a name='header-c155' class='md-header-anchor '></a>文件读取</h3><p>fs.readFile(path,[,options],callback)</p><ul><li><p>path 文件路径 </p></li><li><p>options 可选的字符编码 默认为null</p></li><li><p>callback  回调函数 </p><ul><li>err 错误消息</li><li>data 文件内容 </li></ul></li></ul><pre class='md-fences mock-cm' style='display:block;position:relative'>  fs.readFile(&#39;./files/1.txt&#39;,&#39;utf-8&#39;,(err,dataStr)=&gt;{
    if(err) throw err
    console.log(dataStr)
  })</pre><h3><a name='header-c176' class='md-header-anchor '></a>文件写入</h3><p>fs.writeFile()</p><pre class="md-fences md-end-block" lang="//会覆盖掉之前内容"> <div class="CodeMirror cm-s-inner CodeMirror-wrap"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 0px; left: 4px;"></div><div class="CodeMirror-hscrollbar" style="left: 0px;"><div style="height: 100%; min-height: 1px; width: 0px;"></div></div><div class="CodeMirror-vscrollbar"><div style="min-width: 1px; height: 0px;"></div></div><div class="CodeMirror-scrollbar-filler"></div><div class="CodeMirror-gutter-filler"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; min-height: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines"><div style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><pre class=""><span style="padding-right: 0.1px;">const fs = require("fs")</span></pre></div><pre class=""><span style="padding-right: 0.1px;">fs.writeFile("./file/demo2.txt","荆轲刺秦王123",(err)=&gt;{</span></pre><pre class=""><span style="padding-right: 0.1px;">&nbsp;&nbsp;&nbsp;&nbsp;console.log(err)</span></pre><pre class=""><span style="padding-right: 0.1px;">&nbsp;&nbsp;&nbsp;&nbsp;if(err)&nbsp;return&nbsp;console.log("写入文件失败！"+err.message)</span></pre><pre class=""><span style="padding-right: 0.1px;">&nbsp;&nbsp;&nbsp;&nbsp;console.log('写入文件成功！')</span></pre><pre class=""><span style="padding-right: 0.1px;">})</span></pre></div></div></div></div></div><div style="position: absolute; height: 30px; width: 1px; top: 0px;"></div><div class="CodeMirror-gutters" style="display: none; height: 144px;"></div></div></div></pre><h3><a name='header-c180' class='md-header-anchor '></a>文件追加</h3><p>fs.appendFile()</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const fs = require(&quot;fs&quot;);
fs.appendFile(&quot;./file/demo2.txt&quot;,&quot;\n纸巾&quot;,err=&gt;{
    if(err) console.log(&#39;追加内容失败&#39;+err.message)
    console.log(&#39;追加内容成功！&#39;)
})</pre><h3><a name='header-c184' class='md-header-anchor '></a>文件复制</h3><p>方法1（原理）：</p><pre class='md-fences mock-cm' style='display:block;position:relative'>fs.readFile(&quot;./file/demo2.txt&quot;,&quot;utf-8&quot;,(err,data)=&gt;{
    if(err) console.log(&quot;读取文件失败！&quot;+err.message)
    fs.writeFile(&quot;./file/dome-copy.txt&quot;,data,err=&gt;{
        if(err) console.log(&#39;写入文件失败&#39;+err.message)
        console.log(&#39;写入文件成功！&#39;)
    })
})</pre><p>方法2（推荐）：</p><p>fs.copyFile()</p><pre class='md-fences mock-cm' style='display:block;position:relative'>fs.copyFile(&quot;./file/demo2.txt&quot;,&quot;./file/dome-copy2.txt&quot;,err=&gt;{
    if(err) console.log(&#39;复制文件失败&#39;+err.message)
        console.log(&#39;复制文件成功！&#39;)
})</pre><h3><a name='header-c193' class='md-header-anchor '></a>获取当前文件路径</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//导入文件模块
const fs = require(&quot;fs&quot;)
//倒入路径模块
const path = require(&#39;path&#39;);
//使用path.join()拼接地址（拼接地址可以是当前目录下任意文件）
//__dirname获取当前文件的目录
const newPath = path.join(__dirname,&quot;file/demo2.txt&quot;)
//最后根据文件路径读取文件
fs.readFile(newPath,&quot;utf-8&quot;,(err,data)=&gt;{
    if(err) return console.log(&quot;读取失败！&quot;+err.message)
    console.log(data)
})</pre><p>补充：如果是直接访问当前文件可以：直接使用__filename</p><p>拓展：</p><pre class='md-fences mock-cm' style='display:block;position:relative'>2、读取文件另一种方式
const file = require(&quot;fs&quot;)
const path = require(&quot;path&quot;)
const txtpath = path.join(__dirname,&quot;04函数扩展.js&quot;)
//读取文件
file.stat(txtpath,(err,data)=&gt;{
    if(err) return console.log(&quot;读取文件失败&quot;+message)
    console.log(&quot;文件读取成功！&quot;)
    console.log(data)
})</pre><pre class='md-fences mock-cm' style='display:block;position:relative'>2、读取指定目录中所有文件的名称
const file = require(&quot;fs&quot;)
const path = require(&quot;path&quot;)
file.readdir(__dirname,(err,filename)=&gt;{
    if(err) return console.log(&quot;读取文件名失败&quot;)
    console.log(&quot;读取文件名成功&quot;)
    console.log(filename)
})</pre><h3><a name='header-c201' class='md-header-anchor '></a>返回路径中文件的文件名（不包括后缀）</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//1、获取当前文件路径
const absPath = path.join(__filename)
2、获取.js文件的文件名
console.log(path.basename(absPath，&#39;.js&#39;))</pre><h3><a name='header-c203' class='md-header-anchor '></a>返回文件扩展名</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//1、获取当前文件路径
const absPath = path.join(__filename)
2、获取.js文件的扩展名
console.log(path.extname(absPath))//如果没有扩展名，返回为空</pre><p></p><h3><a name='header-c207' class='md-header-anchor '></a>文件操作案例：</h3><p>获取当前目录下所有文件的信息（文件名、大小、是否为文件），并保存到info.txt文件中</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const file = require(&quot;fs&quot;)
const path = require(&quot;path&quot;)
//1、获取路径下所有文件名称
file.readdir(__dirname, (err, filenames) =&gt; {
    if (err) return console.log(&quot;读取文件名称失败！&quot;)
    let infostr = &#39;&#39;
    let countflag = 0
    //2、循环遍历，所有文件
    filenames.forEach(item=&gt;{
        // (item)所有文件的文件名
        //3、拼接出每个文件的文件路径
        const absPath = path.join(__dirname,item)
        //4、读取所有文件信息
        file.stat(absPath,(err,txtinfo)=&gt;{
            const strinfo = `
            文件名：${item}\n
            文件大小：${txtinfo.size}\n
            是否为文件${txtinfo.isFile()}\n---------------------------\n`
            infostr += strinfo //每次都累加，相当于原有基础上拼接
            //5、将所有文件信息写入，指定文件中
            file.writeFile(path.join(__dirname,&quot;file/info.txt&quot;),infostr,(err)=&gt;{
                 if (err) return console.log(&quot;写入文件失败！&quot;)
                 console.log(&quot;写入文件信息成功&quot;)
            })
        })
    })
})</pre><h1><a name='header-c211' class='md-header-anchor '></a>Javascript</h1><h2><a name='header-c212' class='md-header-anchor '></a>Javascript 是单线程的一门语言</h2><ol start='' ><li>什么是单线程：用户无法主动开启子线程，对于JS的运行来说，永远是主线程在执行关键代码；</li><li>什么是多线程：用户可以主动开启子线程；    Thread  td = new Thread()</li><li>在Node中，操作文件和网络都是比较耗时的操作；</li></ol><h2><a name='header-c223' class='md-header-anchor '></a>Node中为什么大量使用异步方法</h2><ol start='' ><li>为什么要使用 异步方法呢： 因为 异步方法，不会阻塞CPU去执行其它任务；</li><li>为什么在Node中不推荐使用同步呢： 因为 同步，需要一个一个执行，耗时的操作会阻碍CPU执行后续任务，因此，效率慢；</li></ol><h2><a name='header-c231' class='md-header-anchor '></a>CommonJS 模块规范和模块的使用</h2><p>Node.js 实现了 CommonJS 模块化规范；</p><ol start='' ><li><p>什么是 CommonJS 规范？</p><ul><li>CommonJS 是<strong>为了实现 Javascript 的模块化</strong>，而制定的一套规范；</li></ul></li><li><p>为什么 Javascript 需要模块化？</p><ul><li>浏览器中的Javascript有没有实现模块化？（在一个JS文件中，能不能引用另外JS文件中的方法）</li><li>因为有了模块，我们就可以更方便地使用别人的代码，想要什么功能，就加载什么模块。</li></ul></li><li><p>如何实现 Javascript 的模块化？</p><ul><li>为了统一大家编写模块时候的规则，方便各模块之间的依赖和调用，于是 CommonJS 规范就应运而生了。</li></ul></li><li><p>那么，CommonJS 模块化规范，到底是个什么东西？？</p><ul><li>定义了什么是模块</li><li>一个JS模块中，如何引入其它的JS模块</li><li>一个JS模块中，如何向外暴露一些成员，供其它模块调用；</li><li>如果没有类似于 CommonJS 的规范，行不行？</li><li>只有大家遵守同样的规范，才能够协作开发，方便别人，同时也方便自己；</li></ul></li></ol><h2><a name='header-c278' class='md-header-anchor '></a>全局作用域和模块作用域</h2><ol start='' ><li>每个JS文件，就是一个独立的模块，在这个JS文件中，所定义的任何方法、属性、变量、常量、对象，默认都属于 模块作用域，并不会属于 全局作用域；</li><li>如果在某个模块内部，想为 全局的 global 作用域挂载一些属性，需要显示的调用<code>global.***</code>来挂载；</li></ol><h3><a name='header-c286' class='md-header-anchor '></a>global（全局作用域）</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//定义1.js文件并写入代码
var b = &#39;this is txt2&#39;
//将变量 b 设为 全局变量
global.b=b</pre><h3><a name='header-c288' class='md-header-anchor '></a>require（模块引用）</h3><p>每一个实现了 CommonJS 规范的模块，必须定义一个 require() 函数，使用这个 require 函数，就能够 很方便的导入其它 模块中的成员，供自己使用；</p><pre class='md-fences mock-cm' style='display:block;position:relative'>require(&quot;./1.js&quot;)
//只有当模块文件中的变量设置到全局时才能调用，因为被视作闭包
console.log(a)</pre><h3><a name='header-c292' class='md-header-anchor '></a>exports（模块定义）</h3><p>每一个模块中，如果想要把自己的一些私有成员，暴露给别人使用，那么，必须实现一个 exports 对象，这个对象，将来，如果你想把自己的成员，暴露给别人使用，只需要把自己的成员，挂载到 exports 上就行了</p><pre class='md-fences mock-cm' style='display:block;position:relative'>var a = &#39;张三&#39;
//暴露成员
exports.a = a</pre><h3><a name='header-c296' class='md-header-anchor '></a>module（模块标识）</h3><p>这个 module 也是Common JS 规定的，它表示一个具体的模块，也是一个对象；</p><pre class='md-fences mock-cm' style='display:block;position:relative'>var a = 45
//暴露成员
module.exports.a = a</pre><h3><a name='header-c300' class='md-header-anchor '></a>module.exports 和 exports 的关系</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//1.JS
var a = &#39;123&#39;
var b = &#39;126&#39;
module.exports = {a:12}
exports.b = b</pre><p>引用：</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const m1 = require(&quot;./m1.js&quot;)
console.log(m1)
// 都指向同一空对象，但是对外暴露的对象以module为准</pre><p>所以，向外暴露成员时。推荐使用（module.exports）</p><h1><a name='header-c307' class='md-header-anchor '></a>Node结构</h1><h2><a name='header-c308' class='md-header-anchor '></a>Node 中的 Javascript 由几部分组成</h2><ol start='' ><li><p>ECMAScript 核心</p></li><li><p>全局成员</p></li><li><p>模块系统成员</p><ul><li>模块系统成员，根据一些区别，又可以分为三大类： 核心模块、第三方模块、用户自定义模块</li></ul></li></ol><h2><a name='header-c323' class='md-header-anchor '></a>模块分类</h2><h3><a name='header-c324' class='md-header-anchor '></a>核心模块</h3><ol start='' ><li>什么是核心模块：由Node官方提供的好用的模块，叫做核心模块；只要大家在计算机中，安装了Node这个应用程序，那么，我们的计算机中就已经安装了所有的 核心模块；</li><li>如何使用核心模块：<code>require(&#39;核心模块标识符&#39;)</code></li></ol><h3><a name='header-c332' class='md-header-anchor '></a>第三方模块</h3><ol start='' ><li><p>什么是第三方模块：一些非官方提供的模块，叫做第三方模块；注意，第三方模块，并不在我们的计算机上，如果大家需要使用某些第三方模块，必须去一个叫做 <code>NPM</code> 的网站上搜索并下载才能使用；</p></li><li><p>如何使用第三方模块：</p><ul><li>先从 npm 官网上下载指定的第三方模块</li><li>使用 <code>require(&#39;第三方模块的名称标识符&#39;)</code>来导入这个模块</li><li>根据 第三方模块的 官方文档，尝试使用</li></ul></li></ol><h3><a name='header-c350' class='md-header-anchor '></a>用户自定义模块</h3><ol start='' ><li>什么是用户模块：程序员自己写的JS文件，就叫做 用户自定义模块；</li><li>如何使用用户模块：<code>require(&#39;路径标识符&#39;)</code></li></ol><pre class='md-fences mock-cm' style='display:block;position:relative'>const app = require(&#39;./lib/index&#39;)</pre><h2><a name='header-c359' class='md-header-anchor '></a>包</h2><h3><a name='header-c360' class='md-header-anchor '></a>什么是包</h3><ol start='' ><li>英文名叫做 <code>Packages</code>，包是在模块基础上更深一步的抽象，目的是：方便分发推广基于 CommonJS 规范实现的 应用程序 或 类库；</li><li>包可以看作是 模块、代码 和 其它资源 组合起来形成的 独立作用域；</li></ol><h3><a name='header-c368' class='md-header-anchor '></a>规范的包结构</h3><ol start='' ><li><p><strong>包都要以一个单独的目录而存在</strong>；</p></li><li><p><strong><code>package.json</code> 必须在包的顶层目录下</strong>；</p></li><li><p><strong><code>package.json</code> 文件必须符合 JSON 格式，并且必须包含如下三个属性：<code>name</code>, <code>version</code>, <code>main</code></strong></p><ul><li>name:  包的名字</li><li>version: 包的版本号</li><li>main: 表示包的入口文件</li></ul></li><li><p>二进制文件应该在<strong>bin目录</strong>下;</p></li><li><p>javaScript代码应该在<strong>lib目录</strong>下;</p></li><li><p>文档应该在<strong>doc目录</strong>下;</p></li><li><p>单元测试应该在<strong>test目录</strong>下;</p></li><li><p>Node.js对包要求并没有那么严格，只要顶层目录下有<code>package.json</code>，并<strong>符合基本规范</strong>即可;</p></li></ol><pre class='md-fences mock-cm' style='display:block;position:relative'>{
    &quot;name&quot;:&quot;mycalc&quot;,
    &quot;version&quot;:&quot;1.0.0&quot;,
    &quot;main&quot;:&quot;./lib/index.js&quot;
}</pre><p>外界使用就必须先找到main： index.js文件（要引用包应该在包的同级目录下创建app.js文件）</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const app = require(&#39;./lib/index&#39;) //引入自自定义模块
console.log(app)</pre><h3><a name='header-c408' class='md-header-anchor '></a>描述文件 package.json</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>name：包的名称，必须是唯一
description：包的简要说明
version：符合语义化版本识别规范的版本字符串
keywords：关键字数据，通常用于搜索
maintainers：维护者数组，每个元素要包含name、email、web可选字段
contributors：贡献者数组，格式与maintainers相同。包的坐着应该是贡献者数据的第一个元素
bugs：提交bug的地址，可以是网址或者电子邮件地址
licenses：许可证数组，每个元素要包含type和url字段
repositories：仓库托管地址数组，每个元素要包含type、url和path字段
dependencies：包的依赖，一个关联数组，由包名称和版本号组成。
devDependencies：开发依赖项，表示一个包在开发期间用到的依赖项</pre><h2><a name='header-c410' class='md-header-anchor '></a>npm</h2><h3><a name='header-c411' class='md-header-anchor '></a>npm 的两层含义</h3><ol start='' ><li>NPM 是一个 第三方模块的托管网站，指的就是<code>https://www.npmjs.com/</code>；</li><li>NPM 是Node的包管理工具（全名叫做  Node package manager），在我们安装Node时候，就已经顺便也安装了 NPM 这个管理工具；</li></ol><h3><a name='header-c419' class='md-header-anchor '></a>安装和卸载全局包</h3><ol start='' ><li>什么是全局的包：通过  <code>npm install  包名  -g</code> 方式安装的包，都安装到了全局；一般全局的安装目录是<code>C:\Users\自己的用户文件夹\AppData\Roaming\npm</code></li><li><strong>如果拿到一个空项目，必须先初始化一个<code>package.json</code>的配置文件，<code>npm init</code>或者<code>npm init -y</code>*</strong></li><li>运行<code>npm i 包名 --save</code>去安装指定的包，本地安装的包，都安装到了<code>node_modules</code>的目录下</li><li>如果大家用的是npm 5.x的版本，可以不指定<code>--save</code>命令，如果用的是 npm 3.x 的版本，则需要手动指定 <code>--save</code>, 同时，<code>--save</code>有缩写形式，是：<code>-S</code></li><li><code>package-lock.json</code>文件中记录了曾经装过的包的下载地址，方便下次直接下载包；</li></ol><h3><a name='header-c436' class='md-header-anchor '></a>其它常用命令</h3><ol start='' ><li><code>--save-dev</code>它的缩写是<code>-D</code></li><li>注意：<code>dependencies</code>节点，表示项目上线部署时候需要的依赖项；<code>devDependencies</code>节点，表示项目在开发阶段需要的依赖项，但是<strong>当项目要部署上线了</strong>，<code>devDependencies</code>节点中的包<strong>，就不再需要了！</strong></li><li>注意：当使用<code>npm i</code>快速装包的时候，npm会检查<code>package.json</code>文件中，所有的依赖项，然后都为我们安装到项目中</li><li><code>--production</code> 表示只安装 <code>dependencies</code> 节点下，记录的包，不安装<code>devDependencies</code>节点下的包；当项目要上线了，才会使用<code>--production</code>命令</li></ol><h3><a name='header-c450' class='md-header-anchor '></a>解决 npm 下载慢问题</h3><ol start='' ><li>默认，NPM在下载包的时候，连接的是国外的服务器，所以，有时候如果网速不是特别好，可能下载不下来包；此时，大家可以安装一个工具，叫做<code>nrm</code>，里面记录了好多下载NPM包的服务器地址，可以让我们方便的切换下载包时候请求的服务器；</li><li>运行<code>npm i nrm -g</code>（注意：只要是工具，一般都是全局 -g 安装）</li><li>当装完 nrm 之后，可以运行<code>nrm ls</code> 查看所有可用的服务器列表</li><li>可使用<code>nrm use 服务器名称</code>来切换下载包时候的服务器地址</li></ol><p>注意：还可以使用工具yarn来代替此工具，更快！</p><ul><li>npm i yarn -v//和nrm作用一样，下载更快</li><li>yarn add &#39;要安装的包&#39;</li><li>或者 yarn install 安装全局</li></ul><h2><a name='header-c476' class='md-header-anchor '></a>构建自己的web  Server的服务器</h2><ol start='' ><li><h4><a name='header-c479' class='md-header-anchor '></a>导入提供的核心模块</h4><pre class='md-fences mock-cm' style='display:block;position:relative'>const http = require(&quot;http&quot;)</pre></li><li><h4><a name='header-c482' class='md-header-anchor '></a>创建服务器</h4><pre class='md-fences mock-cm' style='display:block;position:relative'>const server = http.createServer();</pre></li><li><h4><a name='header-c485' class='md-header-anchor '></a>为服务器绑定响应事件</h4><pre class='md-fences mock-cm' style='display:block;position:relative'>server.on(&quot;request&quot;,function(req,res){
    console.log(&quot;ok&quot;)
    res.end(&quot;服务器返回数据&quot;)
})</pre></li><li><h4><a name='header-c488' class='md-header-anchor '></a>启动服务器</h4><pre class='md-fences mock-cm' style='display:block;position:relative'>//4、启动服务器
//端口号，ip地址(不写默认监听127.0.0.1),回调函数
server.listen(3000,function(){
    console.log(&quot;通知服务器已经启动！&quot;)
})</pre></li></ol><h2><a name='header-c490' class='md-header-anchor '></a>理解 BS 交互模型</h2><h3><a name='header-c491' class='md-header-anchor '></a>理解 BS 交互模型</h3><p>B/S：表示  Browser / Server        C/S     Client / Server</p><ol start='' ><li>什么是服务器：在网络节点中，专门对外提供资源服务的一台电脑；</li><li>什么是客户端：在网络节点中，专门用来消耗或呈现服务器中返回的数据的电脑；</li><li>什么是静态资源：像 .js ,  .css, .jpg,  .html   ；所谓的静态资源，就是无需数据交互，服务器直接把资源读取，并响应给客户端就完事儿；</li><li>什么是动态资源：当一些资源，服务器上并没有现成的，需要现在服务器端，做一层处理，最后把处理的结果返回给客户端，这样的资源，叫做动态资源；</li><li>HTTP 协议的通信模型：<code>请求 - 处理 - 响应</code>的过程；</li></ol><h3><a name='header-c510' class='md-header-anchor '></a>res和req对象（服务器所绑定的事件中使用）</h3><ul><li><p>返回请求数据</p><pre class='md-fences mock-cm' style='display:block;position:relative'>res.end(&quot;服务器返回数据&quot;)</pre></li><li><p>获取请求路径</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//拿到请求的url地址
console.log(req.url)</pre></li><li><p>获取请求方式</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//拿到请求类型那，toLowerCase转换成小写
console.log(req.method)</pre></li></ul><h3><a name='header-c524' class='md-header-anchor '></a>防止中文乱码（一般放在res.end之前）</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//参数1：数值类型状态码：200成功、300重定向404找不到，500aa类型错误
//参数2：配置对象
res.writeHeader(200,{
    &#39;Content-Type&#39;:&quot;text/html;charset=utf-8&quot;
})//制定编码格式
res.end(&quot;请求类型：&quot;+req.method + &quot;；请求地址：&quot; +req.url)</pre><h3><a name='header-c526' class='md-header-anchor '></a>案例：引入静态资源</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>const http = require(&quot;http&quot;)
const server = http.createServer()
const file = require(&quot;fs&quot;)
const path = require(&quot;path&quot;)
server.on(&quot;request&quot;, (req, res) =&gt; {
    const url = req.url
    getHtml(res, url)
})
server.listen(3000, function () {
    console.log(&quot;http://127.0.0.1:3000&quot;)
})
function getHtml(res, url) {
    // res.writeHead(200, {&quot;Content-Type&quot;:&quot;image/jpeg&quot;});
    file.readFile(path.join(__dirname, url), &#39;utf-8&#39;, (err, data) =&gt; {
        if (err) return res.end(&#39;404，您访问的页面未找到，炸了！！&#39;)
        if (url === &#39;/asset/image/hong.jpg&#39;) {
            res.writeHead(200, {&quot;Content-Type&quot;:&quot;image/jpeg&quot;});//指定资源的编码格式
            res.end(data)
        } else {
            res.end(data)
        }
    })
}</pre><h2><a name='header-c528' class='md-header-anchor '></a>结合模板引擎实现动态资源服务器</h2><h3><a name='header-c529' class='md-header-anchor '></a>导入art -template第三方模块</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>const template = require(&quot;art-template&quot;)</pre><h3><a name='header-c531' class='md-header-anchor '></a>设置页面渲染数据</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//设置页面渲染数据
    const person = {
        name:&#39;熊大大&#39;,
        age:21,
        hobby:[&#39;吃饭&#39;,&#39;睡觉&#39;,&#39;敲代码&#39;]
    }</pre><h3><a name='header-c533' class='md-header-anchor '></a>生成模版字符串</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//参数1为模版文件绝对路径
const htmlStr = template(path.join(__dirname,&#39;asset/index.html&#39;),person)
res.end(htmlStr)</pre><h3><a name='header-c535' class='md-header-anchor '></a>页面渲染</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>&lt;ul&gt;
        &lt;li&gt;姓名：&lt;%=name%&gt;&lt;/li&gt;
        &lt;li&gt;年龄：&lt;%=age%&gt;&lt;/li&gt;
        &lt;li&gt;爱好：
            &lt;ul&gt;
                &lt;li&gt;&lt;%=hobby[0]%&gt;&lt;/li&gt;
                &lt;li&gt;&lt;%=hobby[1]%&gt;&lt;/li&gt;
                &lt;li&gt;&lt;%=hobby[2]%&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
    &lt;/ul&gt;</pre><p>完整案例：</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const http = require(&quot;http&quot;)
const path = require(&quot;path&quot;)
//导入art -template第三方模块
const template = require(&quot;art-template&quot;)
const server = http.createServer()
server.on(&#39;request&#39;,(req,res)=&gt;{
    //设置页面渲染数据
    const person = {
        name:&#39;熊大大&#39;,
        age:21,
        hobby:[&#39;吃饭&#39;,&#39;睡觉&#39;,&#39;敲代码&#39;]
    }
    //参数1为模版文件绝对路径
    const htmlStr = template(path.join(__dirname,&#39;asset/index.html&#39;),person)
    res.end(htmlStr)
})
server.listen(3000,function(){
    console.log(&quot;http://127.0.0.1:3000&quot;)
})</pre><h2><a name='header-c540' class='md-header-anchor '></a>使用 <code>nodemon</code> 工具来自动重启web服务器</h2><ul><li>这个工具的作用：能够实时监听 web 服务器中，代码的改变，只要代码被修改并保存了，则 nodemon 工具，会自动重新启动 web 服务器；</li><li>运行 <code>npm i nodemon -g</code> 就能够在全局环境中，安装这个工具了</li><li>当安装完毕 <code>nodemon</code> 之后，就可以 使用 <code>nodemon 要执行的js文件路径</code> 来运行JS文件了</li><li>今后在开发Web项目的时候，推荐使用 nodemon 来执行 web 服务器</li></ul><h2><a name='header-c554' class='md-header-anchor '></a>Node 中的 Web 快速开发框架 - Express</h2><h3><a name='header-c555' class='md-header-anchor '></a>定义什么是Express：</h3><ol start='' ><li>基于 Node.js 后端Javascript平台之上，开发出来的一套Web开发框架； </li><li>Express中，基于 原生Node的特性，做了进一步的封装，提供了一些更加好用的方法，来提高Web开发的体验；</li><li>Express中，并没有覆盖或者删除原生的http模块方法；</li></ol><h3><a name='header-c566' class='md-header-anchor '></a>express 框架的安装和基本使用</h3><ol start='' ><li><p>直接运行 <code>npm install express --save</code> 就可以安装Express框架了</p></li><li><p>导入模块</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&#39;express&#39;)</pre></li><li><p>创建服务器</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const app = express()</pre></li><li><p>事件监听</p><pre class='md-fences mock-cm' style='display:block;position:relative'>app.get(&#39;/&#39;,function(req,res){
    //这里是原生方法的api
    // res.end(&#39;&lt;h1&gt;这是express中开启的服务器，查看是否会乱码（会）&lt;/h1&gt;&#39;)
    //express独有的，可以直接防止中文乱码问题
    res.send(&#39;&lt;h1&gt;这是express中开启的服务器，查看是否乱码（会）&lt;/h1&gt;&#39;)
})</pre></li><li><p>启动服务器</p><pre class='md-fences mock-cm' style='display:block;position:relative'>app.listen(4000,()=&gt;{
    console.log(&#39;express server running at http://127.0.0.1:4000&#39;)
})</pre></li></ol><h2><a name='header-c587' class='md-header-anchor '></a>服务器事件监听请求说明</h2><ul><li>app.get只监听get请求,app.post。。。</li><li>app.all表示监听所有类型请求</li></ul><h2><a name='header-c595' class='md-header-anchor '></a>express独有的，直接防止中文乱码问题</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>//这里是原生方法的api
// res.end(&#39;&lt;h1&gt;这是express中开启的服务器，查看是否会乱码（会）&lt;/h1&gt;&#39;)
//express独有的，可以直接防止中文乱码问题
res.send(&#39;&lt;h1&gt;这是express中开启的服务器，查看是否乱码（会）&lt;/h1&gt;&#39;)</pre><h2><a name='header-c597' class='md-header-anchor '></a>使用 express 快速托管静态资源</h2><ol start='' ><li>如果我们网站中，有很多静态资源需要被外界访问，express 框架，为我们提供了一个 内置的（中间件）  <code>express.static(&#39;静态资源目录&#39;)</code>  ， 来快速托管指定目录下的<strong>所有静态资源文件；</strong></li><li>用法： <code>app.use(express.static(&#39;public&#39;));</code></li></ol><ul><li>其中， <code>express.static</code> 是一个express的内置中间件；</li><li><code>app.use()</code>方法，是专门用来注册 中间件；</li></ul><ol start='' ><li>当使用 第二步中的方法，把指定目录托管为静态资源目录之后，那么，这一层被托管的目录，不应该出现在 资源访问的 URL地址中；</li><li>在一个Web项目中，我们可以多次调用<code>app.use(express.static())</code></li><li>在多次调用 express.static 的时候，如果文件名称有重复的，则以先注册的中间件为主！</li><li>如果项目要部署了，推荐大家配置一个叫做<code>compression</code>的中间件，它能够开启服务器的GZip压缩功能；</li></ol><h3><a name='header-c625' class='md-header-anchor '></a>1、先导入express框架</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&quot;express&quot;)</pre><h3><a name='header-c627' class='md-header-anchor '></a>2、托管静态资源</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//快速托管静态资源文件，静态文件所在目录作为参数传递即可
app.use(express.static(&#39;./view&#39;));//asset下的页面文件都可访问了
app.use(express.static(&#39;./asset&#39;));//asset下的静态资源文件都可访问了</pre><p>注意：</p><ol start='' ><li>启动服务器后：默认自动启动view目录下的index.html文件</li><li>index.html文件中，填写资源引用路径时就不用再写/asset路径：</li></ol><pre class='md-fences mock-cm' style='display:block;position:relative'> &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/index.css&quot;&gt;
 &lt;script src=&quot;/js/index.js&quot;&gt;&lt;/script&gt;</pre><h2><a name='header-c639' class='md-header-anchor '></a>compression压缩功能</h2><ul><li>帮助网页加载(项目部署阶段使用最佳)</li></ul><h3><a name='header-c644' class='md-header-anchor '></a>安装（项目路径下）</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>npm i compression</pre><h3><a name='header-c646' class='md-header-anchor '></a>导入compression</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>const compression = require(&#39;compression&#39;)</pre><h3><a name='header-c648' class='md-header-anchor '></a>注册压缩资源中间件</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//注册压缩资源中间键
server.use(compression())</pre><h2><a name='header-c650' class='md-header-anchor '></a>虚拟目录 (推荐)</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&#39;express&#39;)
const app = express()
app.use(express.static(&#39;./view&#39;))
//访问资源时可以设置虚拟目录
app.use(&#39;/asset&#39;,express.static(&#39;./asset&#39;))
//这样子文件中就可以通过/static/css/xxx.css的方式访问public下面的内容了
app.listen(3000,()=&gt;{
    console.log(&#39;http://127.0.0.1:3000&#39;)
})</pre><h2><a name='header-c652' class='md-header-anchor '></a>为 express 框架配置ejs模板引擎渲染动态页面</h2><ul><li>安装 ejs 模板引擎<code>npm i ejs -S</code></li><li>使用 app.set() 配置默认的模板引擎 <code>app.set(&#39;view engine&#39;, &#39;ejs&#39;)</code></li><li>使用 app.set() 配置默认模板页面的存放路径 <code>app.set(&#39;views&#39;, &#39;./views&#39;)</code></li><li>使用 res.render() 来渲染模板页面<code>res.render(&#39;index.ejs&#39;, { 要渲染的数据对象 })</code>，注意，模板页面的 后缀名，可以省略不写！</li></ul><h3><a name='header-c666' class='md-header-anchor '></a>注意res.render</h3><p><code>res.render(&#39;index.ejs&#39;, { 要渲染的数据对象 })</code>，模板页面的 后缀名（ejs），可以省略不写！</p><h3><a name='header-c669' class='md-header-anchor '></a>案例</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&#39;express&#39;)
const path = require(&#39;path&#39;)
const app = express()
// 配置模版引擎1步，（express更加适配）
// 使用 app.set() 配置默认的模板引擎
app.set(&#39;view engine&#39;,&#39;ejs&#39;)

// 配置模版引擎2步
// 配置默认模版存放路径
// 注意：默认情况下，无法直接使用，因为express并没有默认为我们提供默认模版引擎
// 所以再调用之前，先认为指定使用哪个模版引擎渲染页面
// 参数1：固定写法，参数2：模版页面存放路径
app.set(&#39;views&#39;,&#39;./view&#39;)

const person = 
    {
        name:&#39;瑞东&#39;,
        age:&#39;21&#39;,
        wife:&#39;西田麻衣&#39;,
        hobby:[&#39;吃饭&#39;,&#39;旅游&#39;,&#39;嘿嘿嘿&#39;]
    }

app.get(&#39;/&#39;,(req,res)=&gt;{
    //配置模版引擎3步
   // sendFile()只能渲染静态页面
    // //express独有的；参数1：要渲染的页面,参数2：要渲染的数据对象 
    // res.render();//渲染动态页面
    res.render(&#39;index.ejs&#39;,person)
})

app.listen(3100,()=&gt;{
    console.log(&#39;http://127.0.0.1:3100&#39;)
})</pre><h2><a name='header-c671' class='md-header-anchor '></a>使用 express 框架中提供的路由来分发请求</h2><ol start='' ><li>什么叫做路由：前端请求的URL地址，都要对应一个后端的处理函数，那么 这种URL地址到 处理函数之间的对应关系，就叫做后端路由；</li><li>在Express中，路由主要负责 分发请求处理的；</li></ol><h3><a name='header-c679' class='md-header-anchor '></a>定义路由</h3><ul><li>创建03.roter.js&#39;文件</li></ul><pre class='md-fences mock-cm' style='display:block;position:relative'>//导入框架
const express= require(&quot;express&quot;)
//创建一个路由对象
const router = express.Router()
const personhu = {
    name:&#39;胡歌&#39;,
    age:&#39;21&#39;,
    wife:&#39;刘亦菲&#39;,
    hobby:[&#39;吃饭&#39;,&#39;旅游&#39;,&#39;嘿嘿嘿&#39;]
}
//发起不同的路由请求
router.get(&#39;/&#39;,(req,res)=&gt;{
    res.render(&#39;rotezhang.ejs&#39;,personhu)
})
router.get(&#39;/routhu&#39;,(req,res)=&gt;{
    res.render(&#39;routhu.ejs&#39;,personhu)
})
router.get(&#39;/routliu&#39;,(req,res)=&gt;{
    res.render(&#39;routliu.ejs&#39;,personhu)
})
//导出路由对象
module.exports = router;//暴露路由对象</pre><h3><a name='header-c685' class='md-header-anchor '></a>使用自己的路由模块渲染页面</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&#39;express&#39;)
const app = express()
//托管静态资源
app.set(&#39;view engine&#39;,&#39;ejs&#39;)
app.set(&#39;views&#39;,&#39;./view&#39;)
// 导入自己的路由模块
const router = require(&#39;./03.roter.js&#39;)
//再将路由对象注册在app服务器中
app.use(router)
app.listen(3000,()=&gt;{
    console.log(&quot;http://127.0.0.1:3000&quot;)
})</pre><h2><a name='header-c687' class='md-header-anchor '></a>中间件 在Express 框架里的概念</h2><p>​	中间件是一个可访问请求对象（<code>req</code>）和响应对象（<code>res</code>）的函数，在 Express 应用的请求-响应循环里，下一个内联的中间件通常用变量 <code>next</code> 表示。中间件的功能包括：</p><ol start='' ><li>中间件是一个函数，路由处理函数</li><li>有一个形参：next：是一个函数</li><li>在中间件中可以调用这个函数</li><li>中间件的表现：函数中的形参列表中只要有next函数就是中间件</li></ol><pre class='md-fences mock-cm' style='display:block;position:relative'>function myMiddleWare(req,res,next){
}</pre><h3><a name='header-c704' class='md-header-anchor '></a>作用：</h3><ul><li><p>表示每一个处理环节</p></li><li><p>这些处理环节之间只负责单独的处理</p></li><li><p>每当上一个中间件处理完以后，都交给下一个中间件继续处理</p></li><li><p>中间件之间共享的是req和res这两个对象</p></li><li><p>在中间件这个函数的参数列表中，有个next函数，这个next函数，只要一</p><p>被调用，就会进入到下一个中间件的处理环节</p></li></ul><h3><a name='header-c723' class='md-header-anchor '></a>案例：</h3><ul><li>日记记录需求：</li><li>每当有客户端来请求服务器，都要记录下这个请求的时间、</li><li>url地址、以及请求的方式。</li><li>格式：2012-12-12  12:12:12  GET/api/postinfo</li></ul><p>分析：</p><ul><li>每次处理的函数中写入一个info.txt文件即可</li></ul><pre class='md-fences mock-cm' style='display:block;position:relative'>//所以服务器启动之后：
//事件绑定get事件(可以把它封装起来)
app.get(&#39;/&#39;,(req,res)=&gt;{
	//调用记录日志方法
    recordLog(req,res)
})

// 封装记录日志方法
function recordLog(req,res){
    const infoStr = `${new Date().toLocaleString()} ${req.method} ${req.url}\n`
    //fs.writeFile：会覆盖；使用fs.appendFile()追加
    file.appendFile(path.join(__dirname,&#39;login/info.txt&#39;),infoStr,err=&gt;{
        if(err) return console.log(&#39;记录日志失败&#39;)
        console.log(&#39;记录日志成功&#39;)
    })
}  </pre><p>通过中间件函数的方式实现上述功能：</p><ul><li><p>创建一个中间件函数</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//定义一个写入日志的中间件
function myMiddleWareWriteLogs(req,res,next){
    // 设置信息的字符串
    const infoStr = `${new Date().toLocaleString()} ${req.method} ${req.url}\n`
    file.appendFile(path.join(__dirname,&#39;login/info.txt&#39;),infoStr,err=&gt;{
        if(err) return console.log(&#39;记录日志失败&#39;)
        console.log(&#39;记录日志成功&#39;)
	    //当记录完毕后，自动进入下一个
	    next();
    })
}</pre></li><li><p>引入这个中间件函数</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//引入中间件函数
app.use(myMiddleWareWriteLogs)</pre><p>结论：这样不用每次请求都要调用记录日志方法，只要发起请求就会触发中间件函数</p></li></ul><h2><a name='header-c757' class='md-header-anchor '></a>如何在路由中拿到post提交过来的表单数据？</h2><ul><li>express并没有这种功能</li></ul><h3><a name='header-c762' class='md-header-anchor '></a>关键思路：</h3><ul><li><strong>每当req的data事件被促发时</strong>，就表示有表单数据过来</li><li>chunk:片、块是buffer二进制类型</li><li>data有可能被促发多次，每次拿到一小块chunk就必须拼接一下</li></ul><h3><a name='header-c773' class='md-header-anchor '></a>自定义模拟解析表单数据</h3><p>先创建并启动服务器</p><p>再创建并引入中间件函数</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&#39;express&#39;)
const file = require(&#39;fs&#39;)
const path = require(&#39;path&#39;)
//创建服务器
const app = express()
//引入中间块函数
app.use(myMiddleWareWriteLogs)
//引入路由
const router = require(&#39;./06.router.js&#39;)
app.use(router)
//启动服务器
app.listen(3001,()=&gt;{
    console.log(&quot;http://127.0.0.1:3001&quot;)
})
function myMiddleWareWriteLogs(req,res,next){
    const infoStr = `模拟表单解析时间:${new Date().toLocaleString()}； 请求方式：${req.method}； 请求地址：${req.url}\n`
    file.appendFile(path.join(__dirname,&#39;login/info.txt&#39;),infoStr,err=&gt;{
        if(err) return console.log(&#39;记录日志失败!&#39;)
        console.log(&#39;记录日志成功!&#39;)
        next()//别忘了
    })
}
module.exports = myMiddleWareWriteLogs</pre><p>引入并启动路由模块（每次请求交给路由模块处理）</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const express = require(&#39;express&#39;)
//引入解析链接参数方法,查询字符串
const querystring = require(&#39;querystring&#39;);
const router = express.Router() //创建路由
router.get(&#39;/&#39;,(req,res)=&gt;{
    res.send(&quot;&lt;h1&gt;这是首页&lt;/h1&gt;&quot;)
})
router.get(&#39;/movie.html&#39;,(req,res)=&gt;{
    res.send(&quot;&lt;h1&gt;这是电影页&lt;/h1&gt;&quot;)
})
router.post(&#39;/about.html&#39;,(req,res)=&gt;{
    // 每当req的data事件被促发时，就表示有表单数据过来
    // chunk:片、块是buffer二进制类型
    // data有可能被促发多次，每次拿到一小块chunk就必须拼接一下
    let dataStr=&#39;&#39;;
        req.on(&#39;data&#39;,(chunk)=&gt;{
        dataStr+=chunk
    })
    req.on(&#39;end&#39;,()=&gt;{
        // console.log(dataStr)
        //解析成对象,node提供了方法
        //参数1：必填，参数二：界定键值对符号，默认’&amp;‘，参数三，默认’=’
        const result = querystring.parse(dataStr)
        console.log(result)
        res.send(&quot;&lt;h1&gt;这是使用post请求服务器数据&lt;/h1&gt;&quot;+JSON.stringify(result))
    })
})
module.exports = router
</pre><p>关键点：</p><ul><li><p>req的data事件</p><p>每当req的data事件被促发时，就表示有表单数据过来</p></li><li><p>引入解析链接参数方法,查询字符串</p><p>解析成对象</p></li></ul><h3><a name='header-c795' class='md-header-anchor '></a>第三方中间件解析表单数据方法</h3><ul><li>运行 npm i body-parse -S 安装解析表单数据的</li><li>导入中间件：const bodyParser = require(&#39;body-parser&#39;)</li><li>注册中间件,解析普通键值对数据，false表示不使用扩展模块解析表单数据而使用node内置的querystring模块来解析表单数据：pp.use(bodyParser.urlencoded({extended:false}))</li></ul><p>自己写的中间件：</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const querystring = require(&#39;querystring&#39;)

function parseForm(req,res,next){
    let dataStr=&#39;&#39;;
    //当表单有数据提交时才触发此事件
    req.on(&#39;data&#39;,(chunk)=&gt;{
        dataStr += chunk
    })
    //数据接收完毕
    req.on(&#39;end&#39;,()=&gt;{
        //如何将result传递给下一个函数呢？
        const result = querystring.parse(dataStr)
        req.body = result
        next()//接入下一函数
    })
}
//以模块化的形式将parseForm方法暴露出去
module.exports = parseForm


调用方法：
// 导入记录自己解析表单日志的中间件
// const parseForm = require(&#39;./07parseFrom.js&#39;)
// //注册解析表单中间键
// app.use(parseForm)</pre><p>第三方中间件（使用）</p><pre class='md-fences mock-cm' style='display:block;position:relative'>// 导入第三方中间块
const bodyParser = require(&#39;body-parser&#39;)
// 3、注册中间件,解析普通键值对数据，false表示不使用扩展模块解析表单数据
// 而使用node内置的querystring模块来解析表单数据
app.use(bodyParser.urlencoded({extended:false}))</pre><h3><a name='header-c812' class='md-header-anchor '></a>路由一定要写在中间件之后</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>//引入路由,路由一定要要写在中间件之后
const router = require(&#39;./07.router.js&#39;)
app.use(router)</pre><h3><a name='header-c814' class='md-header-anchor '></a>中间件的五种分类</h3><ol start='' ><li>应用级：绑定挂载到app对象上的中间件（函数）</li><li>路由级：绑定到Router上</li><li>错误处理：(err,req,res,next)有四个形参的函数</li><li>内置中间件：express.static是唯一的内置中间件</li><li>第三方中间件：通过npm安装的中间件</li></ol><h2><a name='header-c831' class='md-header-anchor '></a>模块加载机制</h2><ol start='' ><li>只要使用require来加载指定模块必然会加载模块中代码</li><li>只要加载过的代码，会默认缓存，下次执行会优先从缓存中加载，提高模块运行效率</li></ol><h3><a name='header-c839' class='md-header-anchor '></a>核心模块加载机制</h3><ol start='' ><li>先从缓存中查找，如果有，则直接使用</li><li>如果缓存中没有，则根据路径标识符，加载本地用户模块并缓存起来，供下次使用</li></ol><h3><a name='header-c847' class='md-header-anchor '></a>第三方模块加载机制</h3><ol start='' ><li>先从缓存中查找，如果有，则直接使用</li><li>如果缓存中没有，则根据第三方模块的路径标识符，加载第三方模块并缓存起来，供下次使用</li></ol><h3><a name='header-c855' class='md-header-anchor '></a>在加载用户模块查找规则</h3><ol start='' ><li>如果没有指定用户后缀名，严格按照指定名称进行查找</li><li>index -&gt; index.js -&gt; index.json(定义常量接收) -&gt; index.node（报错：不是win32）</li></ol><h2><a name='header-c863' class='md-header-anchor '></a>第三方模块(moment)  获取当前日期模块</h2><ul><li>npm i moment，项目路径下安装</li><li>这个函数能不能叫一个包？是，有标准的包结构</li></ul><h2><a name='header-c871' class='md-header-anchor '></a>模块查找规则：</h2><ul><li>1、根据包名称，直接在项目更目录中，去查找一个叫做“node_modules”的文件夹</li><li>2、如果有，则在该文件夹中，继续查找，一个叫做模块引用的文件夹</li><li>3、如果有，则在对应文件夹中查找&#39;package.json&#39;的文件</li><li>4、如果有该文件，则查找其中main属性并尝试加载main指定的文件作为入口、</li><li>5、能正常加载main指定文件则加载成功</li><li>6、如果在packge.json文件中没有main属性，则尝试依次加载根目录中的index.js,index.json,inde.node文件</li><li>7、如果在包的根目录中，根本没有packge.json文件，或者在“node_modules”中没有index相关文件,或者根本没有对应文件夹，或者在项目根目录中根本无node_modules则会向上一层去查找node_modules，查找规则同上</li><li>8、如果上一层目录中还是没有对应模块，则继续上找直到找到项目所在的磁盘根目录中还没找到就报错</li></ul><h1><a name='header-c897' class='md-header-anchor '></a>node中使用mysql数据库</h1><ol start='' ><li><p>安装mysql模块: nom i mysql -S</p></li><li><p>导入模块：const mysql = require(&#39;maysql&#39;)</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//导入数据库模块
const mysql = require(&#39;mysql&#39;)</pre></li><li><p>创建连接对象</p><pre class='md-fences mock-cm' style='display:block;position:relative'>// 创建数据库连接对象
const conn = mysql.createConnection({
    host:&#39;127.0.0.1&#39;,
    user:&#39;root&#39;,
    password:&#39;root&#39;,
    database:&#39;myuser&#39;
})</pre></li><li><p>调用connect()连接上数据库的服务器</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//连接数据库，可不写
conn.connect()</pre></li><li><p>调用conn.query()方法来执行指定的SQL语句</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//执行查询语句
const sqlStr=&#39;select * from info&#39;
conn.query(sqlStr,(err,result)=&gt;{
    console.log(err)
    console.log(result)
})</pre><p>[art-template 官方文档](<a href='https://aui.github.io/art-template/zh-cn/docs/index.html' target='_blank' >https://aui.github.io/art-template/zh-cn/docs/index.html</a></p></li></ol><h2><a name='header-c920' class='md-header-anchor '></a>CEUD操作：增删改查</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>const sqlStr=&quot;insert into info set ?&quot;  //增
const sqlStr=&quot;delete from info  where id = 1&quot;  //删
const sqlStr=&quot;update info set name=&#39;沈阳&#39; where id = 0&quot;  //改</pre><p>在执行语句时，对于后接的参数，可以用问号代替</p><pre class='md-fences mock-cm' style='display:block;position:relative'>const sqlStr=&quot;insert into stu_info set ?&quot;
将参数数据写在 query() 方法中
conn.query(sqlStr,数据对象,(err,result)=&gt;{
    console.log(err)
    console.log(result)
})</pre><p>注意：企业级项目开发，不建议使用delete删除数据，
可以通过添加isdel字段属性来标识数据是否删除（软删除）</p><pre class='md-fences mock-cm' style='display:block;position:relative'>例如：updated users set isdel = 1 where id = 2</pre><h1><a name='header-c929' class='md-header-anchor '></a>express中获取参数的几种形式</h1><h2><a name='header-c930' class='md-header-anchor '></a>从URL地址中获取查询参数</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>app.get(&quot;/index.html&quot;,(req,res)=&gt;{
    res.send(&#39;ok&#39;)
    console.log(req.query)
})
//浏览器输入
http://127.0.0.1:3000/index.html?id=1</pre><h2><a name='header-c932' class='md-header-anchor '></a>从URL地址路径中获取</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>app.get(&quot;/userinfo/:id/:name&quot;,(req,res)=&gt;{
    console.log(req.params)
})
//浏览器输入
http://127.0.0.1:3000/userinfo/1/熊刚</pre><h2><a name='header-c934' class='md-header-anchor '></a>从post表单中获取提交的数据</h2><pre class='md-fences mock-cm' style='display:block;position:relative'>//先引入第三方中间件
const bodyParse = require(&#39;body-parser&#39;)
app.use(bodyParse.urlencoded({extended:false}))
//无法请求
app.get(&quot;/index.html&quot;,(req,res)=&gt;{
    console.log(req.query)
})
浏览器输入：http://127.0.0.1:3000/index.html?id=2&amp;name=熊刚</pre><h1><a name='header-c936' class='md-header-anchor '></a>企业两种开发模式</h1><h2><a name='header-c937' class='md-header-anchor '></a>混合模式</h2><ul><li>前端只需要美化样式，写js特效</li></ul><h2><a name='header-c942' class='md-header-anchor '></a>前后端分离</h2><ul><li>后端负责操作数据库，给前端暴露接口，渲染页面，前端就可以使用一些流行的框架：如vue,react,angluar</li></ul><h2><a name='header-c947' class='md-header-anchor '></a>跨域问题</h2><ul><li>如果不考虑 表单的 Post 提交，则 可以使用 JSONP的形式来请求接口</li><li>但是，我们的项目中，涉及到了 英雄表单的 提交，表单提交一般都是Post</li><li>经过分析，由于JSONP，不支持Post，所以，我们的后端接口，无法设计成JSONP的接口；</li></ul><h2><a name='header-c958' class='md-header-anchor '></a>JSONP 和 CORS 的区别</h2><p>1、JSONP的原理：动态创建script标签；</p><ul><li>JSONP发送的不是Ajax请求</li><li>不支持 Post 请求；</li></ul><p>2.CORS中文意思是<code>跨域资源共享</code> , 本质，就是使用 XHR 对象，发送Ajax请求，来进行跨域的资源共享；</p><ul><li>CORS 发送的是真正的Ajax请求</li><li>CORS 支持Ajax的跨域</li><li>如果要启用 CORS 跨域资源共享，关键在于 服务器端，只要 服务器支持CORS跨域资源共享，则 浏览器肯定能够正常访问 这种 CORS 接口；而且，客户端在 发送 Ajax的时候，就像发送普通AJax一样，没有任何代码上的变化；</li></ul><p>3、对于Node来说，如果想要开启 CORS 跨域通信，只需要安装<code>cors</code>的模块即可；jax的时候，就像发送普通AJax一样，没有任何代码上的变化；</p><pre class='md-fences mock-cm' style='display:block;position:relative'>npm i cors -S
// 导入CORS模块
const cors = require(&#39;cors&#39;)
// 开启客户端跨域服务
app.use(cors())</pre><p></p><h2><a name='header-c985' class='md-header-anchor '></a>英雄接口</h2><h3><a name='header-c986' class='md-header-anchor '></a>准备工作</h3><pre class='md-fences mock-cm' style='display:block;position:relative'>npm init -y初始化项目
npm i express -S
npm i mysql -S</pre><p>1、server文件夹:服务器文件</p><ul><li>server.js服务器端入口文件</li></ul><p>2、web文件夹：页面代码</p><ul><li>web.js页面入口文件</li></ul><h4><a name='header-c1000' class='md-header-anchor '></a>安装</h4><pre class='md-fences mock-cm' style='display:block;position:relative'>//views文件夹-》index.html安装模版引擎
npm i art-template -S
//安装bootstrap美化
npm i bootstrap@3 -S
</pre><p></p><h3><a name='header-c1004' class='md-header-anchor '></a>知识点：</h3><ol start='' ><li><p>res.json();自动把对象转换为json字符串</p></li><li><p>e.preventDefault();阻止表单默认提交</p></li><li><p>location.href=&quot;/&quot;   //跳转回首页</p></li><li><p>$(&quot;form&quot;).serialize()//表单中所有数据，并序列化一下</p><pre class='md-fences mock-cm' style='display:block;position:relative'>$.ajax({
      url:&quot;http://127.0.0.1:5000/api/addhero&quot;,
      data:$(&quot;form&quot;).serialize(),
      type:&quot;post&quot;,
      dataType:&quot;json&quot;,
      success:function(result){
         console.log(result);
      }
})</pre><p>​</p></li></ol><h3><a name='header-c1021' class='md-header-anchor '></a>思路：</h3><ul><li>1、创建一个最基本express服务器，作用：不提供web服务。</li><li>而是提供一个数据接口接口服务；</li><li>2、安装cors模块，从而启用跨域资源共享；</li><li>3、安装mysql模块来操作数据库；</li><li>4、根据api设计文档来创建对应接口，在接口api中，如果返回json</li><li>数据，使用res.json()</li><li>5、再设计更新英雄时安装body-parser中间件，来解析表单数据</li></ul><h3><a name='header-c1044' class='md-header-anchor '></a>如何拿到get提交过来数据</h3><ul><li>const id = req.query.id</li></ul><h3><a name='header-c1049' class='md-header-anchor '></a>第三方模块也可以发送ajax</h3><ul><li>axios专门发送ajax的</li><li>前后端都可以使用，</li><li>基于promis封装的<br/></li></ul><p>安装，将资源托管到页面</p><pre class='md-fences mock-cm' style='display:block;position:relative'>//导入所有功能资源：jquery，axios等
app.use(&#39;/node_modules&#39;,express.static(&quot;../node_modules&quot;))</pre><p>再引用</p><pre class='md-fences mock-cm' style='display:block;position:relative'>&lt;script src=&quot;/node_modules/jquery/dist/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/node_modules/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;</pre><p>发送ajax的时候可以使用：</p><pre class='md-fences mock-cm' style='display:block;position:relative'> // $(&quot;form&quot;).serialize()//表单中所有数据，并序列化一下
                // name=李白&amp;gender=男=》格式
                axios.post(&quot;http://127.0.0.1:5000/api/addhero&quot;,$(&quot;form&quot;).serialize())
                .then(function(result){
                if(result.status == 200 &amp;&amp; result.data.err_code === 0) {
                    location.href=&quot;/&quot;;
                } else {
                    alert(&#39;添加失败！&#39;);
                }
                })</pre><h2><a name='header-c1069' class='md-header-anchor '></a>文章</h2><ul><li><a href='http://www.ruanyifeng.com/blog/2016/04/cors.html'>跨域资源共享 CORS 详解</a></li><li><a href='https://github.com/expressjs/cors'>cors模块解决Node跨域资源共享问题</a></li></ul><h1><a name='header-c1078' class='md-header-anchor '></a>MVC</h1><ol start='' ><li>M:数据操作层(例如:model.js);</li><li>C:业务逻辑操作层（例如：router.js和controller;</li><li>V:views视图层，页面</li></ol><h2><a name='header-c1089' class='md-header-anchor '></a>作用：</h2><ul><li>能够保证每个模块只能单一，提高项目的维护和开发性；</li><li>注意：MVC是后端分层开发思想，所以，MVC是整个项目的角度，去分析统筹前端页面，</li><li>与后端业务逻辑，后端数据操作之间的关系；</li><li>注意：VUE，使用了MVVM的开发思想，只关注前端视图层，只是把每个前端页面</li><li>分成三层，分别是M 、 V 、 VM</li></ul><pre class='md-fences mock-cm' style='display:block;position:relative'>M：保存页面单独数据 
V：每个页面的html结构
VM：调度者，分隔了M和V。每当V层想要获取后保存数据的时候，需要vm做中间处理</pre><p></p></div>
</body>
</html>